#include "ZWindow.h"
#include "ZApplication.h"
#include "ZControl.h"

#undef DrawText

namespace ZuiLib
{
extern ZWindow* ZWindowFromWindow(SDL_Window* wnd);


ZWindow::ZWindow(void)
	:m_win(NULL),m_render(NULL),m_lastColect(0),m_startTime(SDL_GetTicks()),m_tick(0),m_tickCount(0)
{
	ZApplication::Instance()->AddWindow(this);
}


ZWindow::~ZWindow(void)
{
	ZApplication::Instance()->RemoveWindow(this);
	delete m_render;
	SDL_DestroyWindow(m_win);
}

void EventHandler(SDL_Window* wnd,SDL_Event* event)
{
	ZApplication::Instance()->DoMessageWork();
	ZWindow* zWnd=(ZWindow*)SDL_GetWindowData(wnd,WND_USER_DATA);
	SIZE sz=zWnd->GetSize();
	RECT rc={0,0,sz.cx,sz.cy};
	zWnd->Render(&rc);
}


/*
SDL_HITTEST_DRAGGABLE, 
	SDL_HITTEST_RESIZE_TOPLEFT,
	SDL_HITTEST_RESIZE_TOP,
	SDL_HITTEST_RESIZE_TOPRIGHT,
	SDL_HITTEST_RESIZE_RIGHT,
	SDL_HITTEST_RESIZE_BOTTOMRIGHT,
	SDL_HITTEST_RESIZE_BOTTOM,
	SDL_HITTEST_RESIZE_BOTTOMLEFT,
	SDL_HITTEST_RESIZE_LEFT
*/

SDL_HitTestResult WindowHitTest(SDL_Window *win,const SDL_Point *area,void *data)
{
	SDL_HitTestResult rslt=SDL_HITTEST_NORMAL;
	ZWindow* zwnd=(ZWindow*)(data);
	int w,h;
	SDL_GetWindowSize(win,&w,&h);
	
	if(SDL_GetWindowFlags(win)!=SDL_WINDOW_MAXIMIZED ) {
		RECT rcSizeBox = zwnd->GetManager()->GetSizeBox();
		if( area->y < rcSizeBox.top ) {
			if( area->x < rcSizeBox.left ) return SDL_HITTEST_RESIZE_TOPLEFT;
			if( area->x > (w - rcSizeBox.right) ) return SDL_HITTEST_RESIZE_TOPRIGHT;
			return SDL_HITTEST_RESIZE_TOP;
		}
		else if( area->y > (h - rcSizeBox.bottom)) {
			if( area->x <  rcSizeBox.left ) return SDL_HITTEST_RESIZE_BOTTOMLEFT;
			if( area->x >  (w - rcSizeBox.right)) return SDL_HITTEST_RESIZE_BOTTOMRIGHT;
			return SDL_HITTEST_RESIZE_BOTTOM;
		}
		if( area->x < rcSizeBox.left ) return SDL_HITTEST_RESIZE_LEFT;
		if( area->x > (w - rcSizeBox.right)) return SDL_HITTEST_RESIZE_RIGHT;
	}


	RECT rcCaption = zwnd->GetManager()->GetCaptionRect();
	if( area->x < rcCaption.left \
		|| area->x > w - rcCaption.right \
		|| area->y < rcCaption.top \
		|| area->y > h-rcCaption.bottom ) 
	{
		ZuiLib::POINT pt2={area->x,area->y};
		ZControl* pControl = static_cast<ZControl*>(zwnd->GetManager()->FindControl(pt2));
		if( pControl && strcmp(pControl->GetClass(), "Button") != 0 && 
			strcmp(pControl->GetClass(),"Option") != 0 /*&&
			_tcsicmp(pControl->GetClass(), _T("TextUI")) != 0 */)
			return SDL_HITTEST_DRAGGABLE;
	}
	return SDL_HITTEST_NORMAL;
}




bool ZWindow::CreateWindow(const char *title,int x, int y, int w, int h, uint32_t flags)
{
	if (m_win)
		return false;
	m_win= SDL_CreateWindow(title, x,y,w,h,flags | SDL_WINDOW_RESIZABLE);
	
	if (m_win ){
		SDL_SetWindowData(m_win,WND_USER_DATA,this);
#ifdef WIN32
		SDL_SetWindowData(m_win,"event_handler",EventHandler);
#endif
		SDL_SetWindowHitTest(m_win,WindowHitTest,this);

		m_render=new ZRenderer(m_win);
		m_mgr.Init(this);
		Init();
		return true;
	}
	else
		return false;
}


void ZWindow::SetWindowIcon(SDL_Surface *ico)
{
	SDL_SetWindowIcon(m_win,ico);
}

void ZWindow::SetPosition(POINT pt)
{
	SDL_SetWindowPosition(m_win,pt.x,pt.y);
}

POINT ZWindow::GetPosition()
{
	POINT pt={0};
	SDL_GetWindowPosition(m_win,&pt.x,&pt.y);
	return pt;
}

void ZWindow::SetSize(SIZE sz)
{
	SDL_SetWindowSize(m_win,sz.cx,sz.cy);
}

SIZE ZWindow::GetSize()
{
	SIZE sz={0};
	SDL_GetWindowSize(m_win,&sz.cx,&sz.cy);
	return sz;
}


void ZWindow::SetMinimumSize(SIZE sz)
{
	SDL_SetWindowMinimumSize(m_win,sz.cx,sz.cy);
}

SIZE ZWindow::GetMinimumSize()
{
	SIZE sz={0};
	SDL_GetWindowMinimumSize(m_win,&sz.cx,&sz.cy);
	return sz;
}

void ZWindow::SetMaximumSize(SIZE sz)
{
	SDL_SetWindowMaximumSize(m_win,sz.cx,sz.cy);
}

SIZE ZWindow::GetMaximumSize()
{
	SIZE sz={0};
	SDL_GetWindowMaximumSize(m_win,&sz.cx,&sz.cy);
	return sz;
}

void ZWindow::ShowWindow(bool bShow)
{
	if (bShow)
		SDL_ShowWindow(m_win);
	else
		SDL_HideWindow(m_win);
}

void ZWindow::RaiseWindow()
{
	SDL_RaiseWindow(m_win);
}

void ZWindow::SetMaximize()
{
	SDL_MaximizeWindow(m_win);
}
void ZWindow::SetMinimize()
{
	SDL_MinimizeWindow(m_win);
}
void ZWindow::SetRestore()
{
	SDL_MinimizeWindow(m_win);
}

void ZWindow::SetGrab(bool grab)
{
	SDL_SetWindowGrab(m_win,SDL_bool(grab));
}
bool ZWindow::GetGrab()
{
	return SDL_GetWindowGrab(m_win)==SDL_TRUE;
}

bool ZWindow::SetBrightness(float bright)
{
	return SDL_SetWindowBrightness(m_win,bright)==0;
}

void ZWindow::Update(const ZRect * rects,int numrects)
{
	if (!rects)
	{
		SDL_UpdateWindowSurface(m_win);
	}
	else
	{
		SDL_UpdateWindowSurfaceRects(m_win,rects,numrects);
	}
}

void ZWindow::Destroy()
{
	SDL_DestroyWindow(m_win);
}


float ZWindow::GetBrightness()
{
	return SDL_GetWindowBrightness(m_win);
}

void ZWindow::PostClearUpEvent()
{
	SDL_Event event;
	event.type=SDL_USEREVENT;
	event.user.code=SET_CLEAR_UP;
	event.user.windowID=SDL_GetWindowID(m_win);
	SDL_PushEvent(&event);
}


void ZWindow::UserEvent(const SDL_UserEvent& event)
{
	switch(event.code)
	{
	case SET_CLEAR_UP:
		m_mgr.DoDelayedCleanup();
		break;
	default:
		break;
	};
}

void ZWindow::MouseButtonEvent(const SDL_MouseButtonEvent& ev)
{
	POINT pt={ev.x,ev.y};
	ZUIEVENT_TYPE evType=UIEVENT_NONE;
	if(ev.button==1)
	{
		if (ev.type ==SDL_MOUSEBUTTONDOWN)
		{
			if(ev.clicks==1)
				evType=UIEVENT_BUTTONDOWN;
			else
				evType=UIEVENT_DBLCLICK;
		}
		else if (ev.type ==SDL_MOUSEBUTTONUP)
		{
			evType=UIEVENT_BUTTONUP;
		}
	}
	else
	{
		if (ev.type ==SDL_MOUSEBUTTONDOWN)
		{
			if(ev.clicks==1)
				evType=UIEVENT_RBUTTONDOWN;
		}
		else if (ev.type ==SDL_MOUSEBUTTONUP)
		{
			evType=UIEVENT_RBUTTONUP;
		}
	}

	if(evType)
		m_mgr.OnMouseEvent(evType,pt);
}


void ZWindow::FingerEvent(const SDL_TouchFingerEvent& event)
{
	SIZE sz=GetSize();
	POINT pt={event.x * sz.cx,event.y*sz.cy};

	switch(event.type)
	{
	case SDL_FINGERDOWN:
		m_mgr.OnMouseEvent(UIEVENT_BUTTONDOWN,pt);
		break;
	case SDL_FINGERUP:
		m_mgr.OnMouseEvent(UIEVENT_BUTTONUP,pt);
		break;
	case SDL_FINGERMOTION:
		m_mgr.OnMouseEvent(UIEVENT_MOUSEMOVE,pt);
		break;
	}
}


void ZWindow::MouseWheelEvent(const SDL_MouseWheelEvent& ev)
{
	POINT pt={ev.x,ev.y};
	m_mgr.OnMouseEvent(UIEVENT_SCROLLWHEEL,pt);	
}

void ZWindow::MouseMotionEvent(const SDL_MouseMotionEvent& ev)
{
	POINT pt={ev.x,ev.y};
	m_mgr.OnMouseEvent(UIEVENT_MOUSEMOVE,pt);
}

void ZWindow::WindowEvent(const SDL_WindowEvent& event)
{
	switch(event.event)
	{
	case SDL_WINDOWEVENT_ENTER:
		break;
	case SDL_WINDOWEVENT_LEAVE:
		{
			POINT pt={-1,-1};
			m_mgr.OnMouseEvent(UIEVENT_MOUSELEAVE,pt);
		}
		break;
	case SDL_WINDOWEVENT_FOCUS_GAINED:
		{
			ZControl* ctrl=m_mgr.GetFocus();
			if (ctrl)
			{
				TEventUI uiEvent;
				memset(&uiEvent,0,sizeof(uiEvent));
				uiEvent.Type=UIEVENT_SETWINDOWFOCUS;
				uiEvent.dwTimestamp=SDL_GetTicks();
				ctrl->DoEvent(uiEvent);
			}
		}
		break;

	case SDL_WINDOWEVENT_FOCUS_LOST:
		{
			ZControl* ctrl=m_mgr.GetFocus();
			if (ctrl)
			{
				TEventUI uiEvent;
				memset(&uiEvent,0,sizeof(uiEvent));
				uiEvent.Type=UIEVENT_KILLWINDOWFOCUS;
				uiEvent.dwTimestamp=SDL_GetTicks();
				ctrl->DoEvent(uiEvent);
			}
		}
		break;

	case SDL_WINDOWEVENT_SIZE_CHANGED:
	case SDL_WINDOWEVENT_RESIZED:
		m_mgr.Invalidate();
		break;
	}
}

void ZWindow::TextInputEvent(const SDL_TextInputEvent& event)
{
	ZControl* ctrl=m_mgr.GetFocus();
	if (ctrl)
	{
		TEventUI uiEvent;
		memset(&uiEvent,0,sizeof(uiEvent));
		uiEvent.Type=UIEVENT_TEXTINPUT;
		uiEvent.dwTimestamp=SDL_GetTicks();
		uiEvent.wParam= (uint64_t)&event;
		ctrl->DoEvent(uiEvent);
	}
}

void ZWindow::TextEditingEvent(const SDL_TextEditingEvent& event)
{
	ZControl* ctrl=m_mgr.GetFocus();
	if (ctrl)
	{
		TEventUI uiEvent;
		memset(&uiEvent,0,sizeof(uiEvent));
		uiEvent.Type=UIEVENT_TEXTEDITING;
		uiEvent.dwTimestamp=SDL_GetTicks();
		uiEvent.wParam= (uint64_t)&event;
		ctrl->DoEvent(uiEvent);
	}
}

void ZWindow::KeyboardEvent(const SDL_KeyboardEvent& event)
{
	ZControl* ctrl=m_mgr.GetFocus();
	if (ctrl)
	{
		TEventUI uiEvent;
		memset(&uiEvent,0,sizeof(uiEvent));
		if(event.type==SDL_KEYDOWN)
			uiEvent.Type=UIEVENT_KEYDOWN;
		else
			uiEvent.Type=UIEVENT_KEYUP;

		uiEvent.dwTimestamp=SDL_GetTicks();
		uiEvent.wParam= (uint64_t)&event.keysym;
		ctrl->DoEvent(uiEvent);
	}
}

void ZWindow::Init()
{

}

void ZWindow::Render(const RECT* rc)
{

#ifdef DOUBLE_BUFF_DRAW
	RECT rcDirty=m_mgr.GetInvalidateRect();
	if (m_render && rcDirty.right!=rcDirty.left && rcDirty.bottom!=rcDirty.top)
	{
#ifdef SOFTWARE_DRAW
		m_mgr.DoPaint(m_render,*rc);
		m_render->Present();
#else
		SIZE wndSize={rc->right-rc->left,rc->bottom-rc->top};
		if (!m_cacheTexture || m_cacheTexture->GetSize() != wndSize)
		{
			m_cacheTexture=m_render->CreateTexture(SDL_PIXELFORMAT_RGBA8888,SDL_TEXTUREACCESS_TARGET,wndSize.cx,wndSize.cy);
		}
		int rslt=m_render->SetRenderTarget(m_cacheTexture->GetSDLTexture());
		m_mgr.DoPaint(m_render,*rc);
		m_render->SetRenderTarget(NULL);
		m_render->Draw(m_cacheTexture);
		m_render->Present();
#endif
	}
#else
		m_mgr.Invalidate(*rc);
		m_mgr.DoPaint(m_render,*rc);
		m_render->Present();
#endif

}


}//namespace

